using System;
using System.Collections.Generic;
using System.Text;
using Sedodream.Sudoku.Board;
using Sedodream.Sudoku.Players;

namespace Sedodream.Sudoku.Evaluators
{
	public class SimpleEvaluator : IEvaluator
	{
		#region Fields
		private IDictionary<Guid, IList<IMove>> groupMoveMap;
		private static readonly int MaxNumMoves = 1000;

		private bool evaluateMinorScore;
		#endregion

		#region Constructors
		public SimpleEvaluator()
		{
			evaluateMinorScore = true;
		}
		#endregion

		#region IMoveEvaluator Members

		public IScore Evaluate(IPlayer player, IMove move,IList<IMove>moveList)
		{
			if (move == null)
				throw new ArgumentNullException("move");

			IBoard board = new SudokuBoard(move.Board.BoardData);

			if (move.ForcedMove)
			{
				IScore test =new Score(ScoreType.MoveScore);
				test.MajorScore = double.MaxValue - 100;
				test.MinorScore = double.MaxValue - 100;
				return test;
			}
			//return new Score(ScoreType.MoveScore, 1, 1);
			//get the score of the board now
			IScore boardScore = Evaluate(player,board,moveList);
			//Create new board

			//IBoard newBoard = new SudokuBoard(move.Board.BoardData);

			BoardState newState = player.PlayMove(board, move);

			IScore moveScore =new Score(ScoreType.MoveScore);
			if (board.State == BoardState.Failed)
			{
				moveScore =new Score(ScoreType.MoveScore);
				moveScore.MajorScore = double.MinValue;
				moveScore.MinorScore = double.MinValue;
				//return moveScore;
			}
			if (move.ForcedMove)
			{
				moveScore =new Score(ScoreType.MoveScore);
				moveScore.MajorScore = double.MaxValue - 100;
				moveScore.MinorScore = double.MaxValue - 100;
			}
			else if (board.State == BoardState.Solved)
			{
				moveScore =new Score(ScoreType.MoveScore);
				moveScore.MajorScore = double.MaxValue;
				moveScore.MinorScore = double.MaxValue;
				//return moveScore;
			}

			if (moveScore == null)
			{
				//TODO: Can do this better
				IScore newBoardScore = Evaluate(player, board,moveList);
				moveScore.MajorScore = newBoardScore.MajorScore;
				moveScore.MinorScore = newBoardScore.MinorScore;
			}
			//player.UndoMove(move.Board, move);

			if (evaluateMinorScore)
			{
				bool forcedMove;
				int minorScore = GetMinorScoreFor(player, move, moveList, out forcedMove);
				if (forcedMove)
				{
					move.ForcedMove = true;
					IScore forcedScore =new Score(ScoreType.MoveScore);
					forcedScore.MajorScore = double.MaxValue - 100;
					forcedScore.MinorScore = double.MaxValue - 100;
					return forcedScore;
				}

				moveScore.MinorScore = (double)minorScore;
			}
			return moveScore;
		}
		#endregion

		#region IBoardEvaluator Members
		public IScore Evaluate(IPlayer player, IBoard board, IList<IMove> moveList)
		{
			if (board == null)
				throw new ArgumentNullException("board");

			IScore score = new Score(ScoreType.BoardScore);

			if (board.State == BoardState.Failed)
			{
				score.MajorScore = double.MinValue;
				score.MinorScore = double.MinValue;
				return score;
			}
			else if (board.State == BoardState.Solved)
			{
				score.MajorScore = double.MaxValue;
				score.MinorScore = double.MaxValue;
				return score;
			}

			//first get the MajorScore of the score
			//MajorScore=Number of empty locations
			int length = board.BoardData.SideLength;
			int numEmptyCells = 0;
			for (int rowIndex = 0; rowIndex < length; rowIndex++)
			{
				for (int colIndex = 0; colIndex < length; colIndex++)
				{
					if (!board[rowIndex, colIndex].CellValue.HasValue)
						numEmptyCells++;
				}
			}

			//score.MajorScore = 1.0d/numEmptyCells;
			score.MajorScore = board.BoardData.SideLength * board.BoardData.SideLength - numEmptyCells;
			//TODO: Later we can change the MinorScore
			score.MinorScore = 0;

			return score;
			//throw new Exception("The method or operation is not implemented.");
		}

		#endregion

		#region Non-pulbic methods
		protected IDictionary<Guid, IList<IMove>> GroupMoveMap
		{
			get
			{
				if (this.groupMoveMap == null)
					groupMoveMap = new Dictionary<Guid, IList<IMove>>();
				return groupMoveMap;
			}
		}
		/// <summary>
		/// Gets the list of all moves in the group.
		/// </summary>
		protected IList<IMove> GetGroupMoves(ICellGroup group, IList<IMove> moveList)
		{
			if (group == null)
				throw new ArgumentNullException("group");
			if (moveList == null)
				throw new ArgumentNullException("moveList");
			//first see if its on the map already

			if (GroupMoveMap.ContainsKey(group.GroupId))
				return GroupMoveMap[group.GroupId];
			//look through the list of moves & determine what group they are in
			IList<IMove> moveInGroup = new List<IMove>();
			foreach (IMove move in moveList)
			{
				if (group.IsPointInGroup(move.Location))
					moveInGroup.Add(move);
			}
			//add this to the map now
			GroupMoveMap[group.GroupId] = moveInGroup;
			return moveInGroup;
		}
		protected virtual int GetMinorScoreFor(IPlayer player, IMove move, IList<IMove> moveList,out bool forcedMove)
		{
			forcedMove = false;
			if (player == null)
				throw new ArgumentNullException("player");
			if (move == null)
				throw new ArgumentNullException("move");
			if (moveList == null)
				throw new ArgumentNullException("moveList");

			//look through each group
			IList<ICellGroup> cellGroups = move.Board.FindAllGroupsContaining(move.Location);

			int score = 0;
			int numMovesInAllGroups = 0;
			foreach (ICellGroup group in cellGroups)
			{
				int numInThisGroup = 0;
				int numWithThisValue = 0;
				IList<IMove> groupMoves = GetGroupMoves(group, moveList);
				if (groupMoves.Count <= 0)
				{
					string message = string.Format("Invalid move evaluation state");
					throw new ApplicationException(message);
				}
				if (groupMoves.Count == 1)
				{
					//if this is the only move in this group we know it is forced.
					move.ForcedMove = true;
					forcedMove = true;
					return int.MaxValue;
				}
				foreach (IMove currentMove in groupMoves)
				{
					numMovesInAllGroups++;
					numInThisGroup++;
					if (currentMove.Value == move.Value)
						numWithThisValue++;
				}
				if (numWithThisValue == 0)
				{
					string message = string.Format("Invalid move evaluation state");
					throw new ApplicationException(message);
				}
				if (numWithThisValue == 1)
				{
					move.ForcedMove = true;
					forcedMove = true;
					return int.MaxValue;
				}

				//determine score for this group and add it to the current score
//				int numInThisGroup = 0;
//				int numWithThisValue = 0;
				int currentScore = 5*(MaxNumMoves-numWithThisValue)
									+ 4 * (MaxNumMoves-numMovesInAllGroups);
				if (currentScore <= 0)
				{
					string message = string.Format("Invalid move evaluation state. Current score <= 0");
					throw new ApplicationException(message);
				}
				score += currentScore;
			}
			return score;
			
		}

		#endregion
	}
}






